package de.slothsoft.commons.swing.window;

import java.awt.GridBagLayout;
import java.awt.Window;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import de.slothsoft.commons.Option;
import de.slothsoft.commons.Option.ValueType;
import de.slothsoft.commons.res.CommonImages;
import de.slothsoft.commons.res.CommonMessages;
import de.slothsoft.commons.swing.validation.ValidationUtil;

public class OptionDialog extends AbstractDialog {

	private static final long serialVersionUID = 1L;

	private List<Option> options;
	private List<JComponent> components;
	private Map<String, Object> valueMap;
	private Map<String, Object> resultingValueMap;

	public OptionDialog(Window parent, String title, List<Option> options) {
		this(parent, title, options, new HashMap<String, Object>());
	}

	public OptionDialog(Window parent, String title, List<Option> options,
			Map<String, Object> valueMap) {
		super(parent);
		this.options = options;
		this.valueMap = valueMap;

		setTitle(title);
		setIconImage(CommonImages.get(CommonImages.TOOLBAR_OPTIONS).getImage());

		pack();
		centerDialog();
	}

	public List<JComponent> getInputComponents() {
		return this.components;
	}

	public List<Option> getOptions() {
		return this.options;
	}

	@Override
	protected void createMainPanel(JPanel mainPanel) {
		mainPanel.setLayout(new GridBagLayout());
		int y = 0;
		this.components = new ArrayList<JComponent>();
		for (Option option : this.options) {
			int x = 0;
			JLabel nameLabel = createLabel(mainPanel, option.getName(), x++, y);

			JLabel descriptionLabel = null;
			if (option.getDescription() != null) {
				descriptionLabel = createLabel(mainPanel,
						CommonImages.get(CommonImages.QUICK_TIP), x, y);
			}
			x++;

			JComponent component = createInputField(mainPanel,
					option.getValueType(), x++, y);

			if (component != null) {
				this.components.add(component);
				Object value = this.valueMap.get(option.getKey());
				if (value == null) {
					value = option.getDefaultValue();
				}
				if (value != null) {
					setValue(component, value);
				}

				if (option.getDescription() != null) {
					nameLabel.setToolTipText(option.getDescription());
					component.setToolTipText(option.getDescription());
					descriptionLabel.setToolTipText(option.getDescription());
				}
			}
			y++;
		}
	}

	protected JComponent createInputField(JPanel parent, ValueType valueType,
			int x, int y) {
		JComponent result = null;
		switch (valueType) {
		case STRING:
		case INTEGER:
			result = createText(parent, x, y);
			break;
		case MULTI_LINE_STRING:
			result = createTextArea(parent, x, y);
			break;
		case BOOLEAN:
			result = createCheckBox(parent, x, y);
			break;
		}
		return result;
	}

	// TODO: every switch should use an adapter

	protected void setValue(JComponent component, Object value) {
		if (component instanceof JTextField) {
			((JTextField) component).setText(value == null ? null : value
					.toString());
		} else if (component instanceof JCheckBox) {
			((JCheckBox) component).setSelected(value == null ? false
					: ((Boolean) value).booleanValue());
		} else if (component instanceof JTextArea) {
			((JTextArea) component).setText(value == null ? null : value
					.toString());
		}
	}

	protected Object getValue(JComponent component, ValueType valueType) {
		if (component instanceof JTextField) {

			String text = ((JTextField) component).getText();
			if (valueType == ValueType.STRING)
				return text;
			// TODO: that should maybe be checked...
			return Integer.parseInt(text);
		} else if (component instanceof JCheckBox)
			return Boolean.valueOf(((JCheckBox) component).isSelected());
		else if (component instanceof JTextArea)
			return ((JTextArea) component).getText();
		return null;
	}

	private String getValidationError(JComponent component, ValueType valueType) {
		if (component instanceof JTextField) {
			if (valueType == ValueType.INTEGER)
				return getIntegerValidationError(component);
		}
		return null;
	}

	private String getIntegerValidationError(JComponent component) {
		String text = ((JTextField) component).getText();
		try {
			Integer.parseInt(text);
			return null;
		} catch (Exception e) {
			return CommonMessages.getString("Validation.noInteger");
		}
	}

	@Override
	protected void performOkAction() {
		if (fillResultMap()) {
			super.performOkAction();
			close();
		}
	}

	private boolean fillResultMap() {
		this.resultingValueMap = new HashMap<String, Object>();
		int size = getInputComponents().size();
		boolean allValid = true;
		for (int i = 0; i < size; i++) {
			JComponent component = getInputComponents().get(i);
			Option option = getOptions().get(i);
			String errorMessage = getValidationError(component,
					option.getValueType());
			if (errorMessage == null) {
				Object value = getValue(component, option.getValueType());
				this.resultingValueMap.put(option.getKey(), value);
			} else {
				allValid = false;
			}
			ValidationUtil.style(component, errorMessage == null, errorMessage);
		}
		return allValid;
	}

	public Map<String, Object> getResultingValueMap() {
		return this.resultingValueMap;
	}
}
